`timescale 1 ns / 1 ns
module tb_CodeConverter;
  
  // external inputs
  reg         clock = 1'b0;           // clock
  reg [3:0]   btn   = 4'hf;           // 4 push buttons
  reg         reset = 1'b0;           // reset button

  // output  
  wire [8:0] compassHeading_out;     // output from the button press decoder
  reg  [8:0] compassHeading;         // output from the button press decoder
  
  // Code Converter
  CodeConverter codeCvt (
    .clk(clock),                    // connect clock
    .pbtn_db(btn),                  // connect push buttons
    .reset(reset),
    .compCounter(compassHeading_out)// connect output
  ); 

/*******************************************************************************
                              Helper Functions
*******************************************************************************/
  // Definition of the buttons
  localparam BTN_LEFT_FWD = 4'b1000;
  localparam BTN_LEFT_REV = 4'b0100;
  localparam BTN_RGHT_FWD = 4'b0010;
  localparam BTN_RGHT_REV = 4'b0001;
  localparam BTN_CENTER   = 4'b0000;

  // Definition of possible button inputs
  localparam btn_in_stopMotor   = 4'b1111;  
  localparam btn_in_movFWD      = BTN_LEFT_FWD | BTN_RGHT_FWD;
  localparam btn_in_movREV      = BTN_LEFT_REV | BTN_RGHT_REV;
  localparam btn_in_rotCW_1x_1  = BTN_LEFT_FWD; // CW rot 1X = button left forward
  localparam btn_in_rotCW_1x_2  = BTN_RGHT_REV; // CW rot 1X = button right reverse
  localparam btn_in_rotCW_2x    = BTN_LEFT_FWD | BTN_RGHT_REV; // CW rot 2X = button right reverse + left forward
  localparam btn_in_rotCCW_1x_1 = BTN_RGHT_FWD; // CCW rot 1X = button right forward
  localparam btn_in_rotCCW_1x_2 = BTN_LEFT_REV; // CCW rot 1X = button left reverse
  localparam btn_in_rotCCW_2x   = BTN_LEFT_REV | BTN_RGHT_FWD; 

  // Function to convert button inputs to human-readable strings
  function [127:0] btn2str( input [3:0] btn_code ) ;
    begin 
      case(btn_code)
        btn_in_stopMotor   : btn2str = "StopMotor";
        btn_in_movFWD      : btn2str = "MoveFWD";
        btn_in_movREV      : btn2str = "MoveREV";
        btn_in_rotCCW_2x   : btn2str = "ROT_CCW_2x";        
        btn_in_rotCW_2x    : btn2str = "ROT_CW_2x ";
        btn_in_rotCW_1x_1  : btn2str = "ROT_CW_1x";
        btn_in_rotCW_1x_2  : btn2str = "ROT_CW_1x";
        btn_in_rotCCW_1x_1 : btn2str = "ROT_CCW_1x ";
        btn_in_rotCCW_1x_2 : btn2str = "ROT_CCW_1x";
      endcase
    end
  endfunction

  // Parameter definitions for decoded output
  localparam movFWD      = 3'd1;   
  localparam movREV      = 3'd2;   
  localparam rotCW_2x    = 3'd3;   
  localparam rotCCW_2x   = 3'd4; 
  localparam rotCW_1x_1  = 3'd5;  
  localparam rotCW_1x_2  = 3'd5; 
  localparam rotCCW_1x_1 = 3'd6; 
  localparam rotCCW_1x_2 = 3'd6; 
  localparam stopMotor   = 3'd7; 

  // Function to convert button press decoded output to human-readable strings  
  function [127:0] mcode2str( input [2:0] m_code ) ;
    begin 
      case(m_code)
        stopMotor   : mcode2str = "StopMotor";
        movFWD      : mcode2str = "MoveFWD";
        movREV      : mcode2str = "MoveREV";
        rotCW_2x    : mcode2str = "ROT_CW_2x";
        rotCCW_2x   : mcode2str = "ROT_CCW_2x";
        rotCW_1x_1  : mcode2str = "ROT_CW_1x ";
        rotCW_1x_2  : mcode2str = "ROT_CW_1x ";
        rotCCW_1x_1 : mcode2str = "ROT_CCW_1x";
        rotCCW_1x_2 : mcode2str = "ROT_CCW_1x";        
      endcase
    end
  endfunction

/*******************************************************************************
                          End of Helper Functions
*******************************************************************************/

  // Define standard  interval
  parameter IVL = 100;

  // Generate 100MHz clock
  always #5 clock = ~clock;

  always @(compassHeading_out) begin
    compassHeading <= compassHeading_out;
  end  
  
  // Wheneven button press output changes 
  // print the button inputs and decoded output to human readable strings
  always @(compassHeading) begin
    $display ("in = %s, out = %d \n", btn2str(btn), compassHeading);
  end

  initial begin
      #IVL btn    = btn_in_movREV;
      #IVL btn    = btn_in_movFWD; 
      #IVL btn    = btn_in_stopMotor;   
      #IVL btn    = btn_in_rotCW_1x_1;
      #IVL btn    = btn_in_rotCW_1x_1;
      #IVL btn    = btn_in_rotCCW_1x_1;
      #IVL btn    = btn_in_rotCCW_1x_1;
      #IVL btn    = btn_in_rotCCW_1x_1;           
      #IVL btn    = btn_in_stopMotor;
      #IVL btn    = 4'b0100; // release right rev - turn left 1x

      // #IVL btn = 4'b0010; // push right fwd    - turn left 1x
      // #IVL btn = 4'b1010; // push left fwd     - move forward
      // #IVL btn = 4'b1000; // release right fwd - turn right 1x
      // #IVL btn = 4'b1001; // push right rev    - turn right 2x
      // #IVL btn = 4'b0001; // release left fwd  - turn right 1x 
      // #IVL btn = 4'b0101; // push left rev     - move in reverse 
      // #IVL btn = 4'b0100; // release right rev - turn left 1x
      // #IVL btn = 4'b0110; // push right fwd    - turn left 2x
      // #IVL btn = 4'b0000; // release all       - stopped
      // #IVL btn = 4'b0011; // right fwd and rev cancel - stopped         
      // #IVL btn = 4'b1100; // left fwd and rev cancel  - stopped  
      // #IVL btn = 4'b1111; // push all cancels         - stopped   

      #IVL $stop;
  end

endmodule
